
#include <string.h>
#include "Define.h"
#include "Package.h"
#include "MQTTNode.h"
#include "MsgHandler.h"
#include "Core/Logger.h"
#include "MqttValidor.h"
#include "MQTT/Params.h"
#include "MQTT/MQTTError.h"
#include "Core/Interface.h"

#define MQTT_USER_PROPERTY_KEY_MAX_LEN         (128)  /* max key length for user property */
#define MQTT_USER_PROPERTY_VALUE_MAX_LEN       (128)  /* max value length for user property */

void mqttFlowControlInc(MQTTEntity *entity) {

    if (entity->flowControlEnabled == 0) {
        return;
    }

    getSystemInterface()->enterMutex(entity->pubMutex);
    entity->serverReceiveMax++;
    entity->serverReceiveMax = (entity->serverReceiveMax > DEFAULT_SERVER_RECEIVE_MAX)? DEFAULT_SERVER_RECEIVE_MAX : entity->serverReceiveMax;

    getSystemInterface()->leaveMutex(entity->pubMutex);
}

void mqttCheckFlowDec(MQTTEntity *entity) {

    if (entity->serverReceiveMax <= 0) {
        return;
    }

    getSystemInterface()->enterMutex(entity->pubMutex);
    entity->serverReceiveMax--;
    getSystemInterface()->leaveMutex(entity->pubMutex);
}

int32_t mqttCheckFlowControl(MQTTEntity *entity, uint8_t qos) {

    if (entity->flowControlEnabled == 0 || qos == MQTT_QOS0) {
        return aiotSuccess;
    }

    uint16_t serverReceiveMax = 0;
    getSystemInterface()->enterMutex(entity->pubMutex);
    serverReceiveMax = entity->serverReceiveMax;
    getSystemInterface()->leaveMutex(entity->pubMutex);

    if (serverReceiveMax <= 0) {
        return mqttRecvMaxExceeded;
    }

    return aiotSuccess;
}

int32_t mqttCheckTopicValid(char *topic, uint32_t len) {

    uint32_t idx = 0;
    /** topic should start with '/' */
    if (topic[0] != '/') {
        return mqttTopicInvalid;
    }

    for (idx = 0; idx < len; idx++) {
        if (topic[idx] == '+') {
            /** topic should contain '/+/' in the middle or '/+' in the end */
            if ((topic[idx - 1] != '/') ||
                ((idx + 1 < len) && (topic[idx + 1] != '/'))) {
                return mqttTopicInvalid;
            }
        }

        if (topic[idx] == '#') {
            /** topic should contain '/#' in the end */
            if ((topic[idx - 1] != '/') ||
                (idx + 1 < len)) {
                return mqttTopicInvalid;
            }
        }
    }

    return aiotSuccess;
}

uint16_t getMqttPacketId(MQTTEntity *entity) {

    uint16_t packetId = 0;
    getSystemInterface()->enterMutex(entity->aiotEntity.dataMutex);
    if ((uint16_t)(entity->packetId + 1) == 0) {
        entity->packetId = 0;
    }

    packetId = ++entity->packetId;
    getSystemInterface()->leaveMutex(entity->aiotEntity.dataMutex);
    return packetId;
}

int32_t mqttCheckTXPayloadLen(MQTTEntity *entity, uint32_t pktLen) {

    if (pktLen >= entity->txPacketSize) {
        aiotExecCountDec(&entity->aiotEntity);
        return mqttInvalidTxPacketSize;
    }

    return aiotSuccess;
}

int32_t checkAuthInfo(MQTTEntity* entity) {

    if (entity->aiotEntity.registerMode == unknownRegMode) {
        return registerModeNoDefine;
    }

    if (entity->aiotEntity.registerMode == thirdTuple) {

        if (entity->aiotEntity.aiotAccess.productKey == nullptr) {
            return paramsMissingProductKey;
        }

        if (entity->aiotEntity.aiotAccess.deviceKey == nullptr) {
            return paramsMissingDeviceName;
        }

        if (entity->aiotEntity.aiotAccess.secretKey == nullptr) {
            return paramsMissingDeviceSecret;
        }

    } else {

        if (entity->aiotEntity.aiotDeviceInfo.productKey == nullptr) {
            return paramsMissingProductKey;
        }

        if (entity->aiotEntity.aiotDeviceInfo.deviceNo == nullptr) {
            return paramsMissingDeviceNo;
        }

        if (entity->aiotEntity.aiotDeviceInfo.secretKey == nullptr) {
            return paramsMissingDeviceSecret;
        }
    }

    return aiotSuccess;
}

int32_t mqttGeneralPropertyValid(GeneralProperty *property) {

    uint32_t idx = 0;
    if (nullptr == property) {
        return aiotSuccess;
    }

    /** User Property */
    UserProperty**  userProps = property->userProperty;

    do {

        UserProperty *userProp = userProps[idx++];
        if (userProp == nullptr) {
            continue;
        }

        if (userProp->key.len > MQTT_USER_PROPERTY_KEY_MAX_LEN ||
            userProp->value.len > MQTT_USER_PROPERTY_VALUE_MAX_LEN) {
            return mqttInvalidUserPerpertyLen;
        }

        if ((userProp->key.len > 0 && nullptr == userProp->key.value) ||
            (userProp->value.len > 0 && nullptr == userProp->value.value)) {

            return mqttInvalidUserPerpertyData;
        }

    } while (idx < MAX_USER_PROPERTY);

    /** Subscription Identifier */
    if (property->subIdentifier > 0xFFFFFFF) {
        return mqttInvalidSubIdentifier;
    }

    return aiotSuccess;
}

uint32_t getGeneralPropertyLen(GeneralProperty *generalProp) {

    uint32_t propertyLen = 0;
    uint8_t subIdentifier[4] = {0},iter = 0;
    uint32_t subIdentiferOffset = 0;
    UserProperty** list = generalProp->userProperty;

    if (generalProp->topicAlias > 0) {
        /** Topic Alias */
        propertyLen += MQTT_V5_PROPERTY_ID_LEN + MQTT_V5_TOPIC_ALIAS_LEN;
    }

    if (generalProp->maxTopicAlias > 0) {
        propertyLen += MQTT_V5_PROPERTY_ID_LEN + MQTT_V5_TOPIC_ALIAS_MAX_LEN;
    }

    /** receive max */
    if (generalProp->maxClientReceive > 0) {
        propertyLen += MQTT_V5_PROPERTY_ID_LEN + MQTT_V5_RECEIVE_MAX_LEN;
    }

    /** User Properties */
    for (iter = 0; iter < MAX_USER_PROPERTY; iter++) {
        UserProperty *user_prop = list[iter];
        if (nullptr != user_prop) {
            propertyLen += MQTT_V5_PROPERTY_ID_LEN;         /** length of user property ID */
            propertyLen += MQTT_V5_USER_PROPERTY_KEY_LEN;   /** length of user property key's length */
            propertyLen += user_prop->key.len;
            propertyLen += MQTT_V5_USER_PROPERTY_VALUE_LEN; /** length of user property value's length */
            propertyLen += user_prop->value.len;
        }
    }

    /** Response Topic */
    if (0 != generalProp->responseTopic.len) {
        propertyLen += MQTT_V5_PROPERTY_ID_LEN + MQTT_V5_RESPONSE_TOPIC_LEN + generalProp->responseTopic.len;
    }

    /** Corelation Data */
    if (0 != generalProp->correlationData.len) {
        propertyLen += MQTT_V5_PROPERTY_ID_LEN + MQTT_V5_CORELATION_DATA_LEN + generalProp->correlationData.len;
    }

    /** Subscription Identifier */
    if (generalProp->subIdentifier > 0) {
        encodeRemainLen(generalProp->subIdentifier, &subIdentifier[0], &subIdentiferOffset);
        propertyLen += MQTT_V5_PROPERTY_ID_LEN + subIdentiferOffset;
    }

    MQTTValue *reasonString = generalProp->reasonString;
    if (nullptr != reasonString) {
        propertyLen += reasonString->len + MQTT_V5_PROPERTY_ID_LEN + MQTT_V5_REASON_STRING_LEN;
    }

    return propertyLen;
}

void cleanMqttSign(MQTTEntity *entity) {

    if (entity->username) {
        getSystemInterface()->memFree(entity->username);
        entity->username = nullptr;
    }

    if (entity->password) {
        getSystemInterface()->memFree(entity->password);
        entity->password = nullptr;
    }

    if (entity->clientId) {
        getSystemInterface()->memFree(entity->clientId);
        entity->clientId = nullptr;
    }
}
